package bcm.bridge.center.printer;

import bcm.printbridge.*;
import bcm.security.BridgeSecurity;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.commons.net.util.Base64;
import org.apache.thrift.TException;
import org.apache.thrift.protocol.TProtocol;
import org.apache.thrift.transport.TSocket;

import java.net.SocketTimeoutException;
import java.nio.ByteBuffer;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

public class PrintBridgeThread extends Thread{
	private long executeWaitTimeout=10000;
	private Lock lock=new ReentrantLock();
	private String bridgeId;
	private PrintBridge.Client client;
	private BridgeState bridgeState;

	private BridgeSecurity security;
	
	
	Log logger=LogFactory.getLog(getClass());

//	private Socket socket;
	private TSocket socket;
	private TProtocol protocol; 

	boolean stop=false;
	private long pingInterval=30000;
	
	public String getBridgeId(){
		return bridgeId;
	}

	
	public BridgeState getBridgeState(){
		return bridgeState;
	}
	
	synchronized public void setStop(){
		stop=true;
	}
	public PrintBridgeThread(TProtocol protocol,TSocket socket,BridgeSecurity security){
		this.protocol=protocol;
		this.socket=socket;
		this.security = security;
	}
	
	public void run(){
		long tick=System.currentTimeMillis();
		try{
			logger.info("remote print client accepted ".concat(socket.toString()));
			if(!InvertBridgeManager.printBridgeThreadLocker.tryAcquire(1,TimeUnit.MILLISECONDS)){
				socket.write("client count exceed\r\n".getBytes());
				socket.flush();
				socket.close();
				return;	
			}			
//			TSocket tsocket=new TSocket(socket);
			socket.setTimeout(30000);
//			TProtocol protocol=new TBinaryProtocol(tsocket);
			if(protocol.readByte()!=1) throw new Exception("protocol error11 ".concat(socket.toString()));
			String _bridgeId=new String(Base64.decodeBase64(protocol.readString()),"utf8");			
			String _uid=new String(Base64.decodeBase64(protocol.readString()),"utf8");
			String _pwd=new String(Base64.decodeBase64(protocol.readString()),"utf8");
			if(protocol.readByte()!=4) throw new Exception("protocol error12 ".concat(socket.toString()));
			
			if(!_bridgeId.startsWith("B:")||_bridgeId.length()<3) throw new Exception("bridgeId error");
			bridgeId=_bridgeId.substring(2);
			if(!_uid.startsWith("U:")||_uid.length()<3) throw new Exception("uid error");
			String uid=_uid.substring(2);
			if(!_pwd.startsWith("P:")||_pwd.length()<3) throw new Exception("pwd error");
			String pwd=_pwd.substring(2);
			
//			User user=UserManageService.getInstance()._getUser(uid);
//			if(user==null||!user.isEnabled()) throw new Exception("user not exists or invalidate");
//			if(!user.getPassword().equals(DigestUtils.md5Hex(pwd))) throw new Exception("password error");

			if (!this.security.auth("printBridge",uid,pwd)) {
				throw new Exception("username or password error");
			}

			protocol.writeBool(true);	
			socket.flush();
			logger.info("remote print client started [".concat(bridgeId).concat("]socket:").concat(socket.toString()));
			synchronized (InvertBridgeManager.printBridgeThreads){
				InvertBridgeManager.printBridgeThreads.put(bridgeId,this);
				bridgeState=new BridgeState();
				bridgeState.setUpTime(new Date());
				bridgeState.setOnline(true);
				bridgeState.setAddress(socket.toString());
				bridgeState.setId(bridgeId);
				InvertBridgeManager.onlinePrintBridgeStateMap.put(bridgeId, bridgeState);
			}				
			
			client=new PrintBridge.Client(protocol);					
			while(!stop&&!isInterrupted()){					
				try {
					Thread.sleep(100);
					synchronized (InvertBridgeManager.printBridgeThreads){
						Thread thread=InvertBridgeManager.printBridgeThreads.get(bridgeId);
						if(thread==null||thread.getId()!=getId()) {
							logger.info(String.format("remote print bridge thread %d,socket:%s is not active,kick it out...",getId(),socket.toString()));
							client.kickOut();
							break;
						}
					}
					if(lock.tryLock(executeWaitTimeout,TimeUnit.MILLISECONDS)){
						try{
							if(System.currentTimeMillis()>tick+pingInterval){
								tick=System.currentTimeMillis();				
								client.ping();								
							}
						}finally{
							lock.unlock();
						}
					}
				} catch (InterruptedException e) {
					break;
				} catch (TException e) {
					if(e.getCause()!=null&&e.getCause() instanceof SocketTimeoutException){
						logger.info("bridge [".concat(bridgeId).concat("] socket timeout,close it"));
						break;
					}else{
						logger.info("client terminated");
						break;
					}
				}					
			}				
	
		} catch (Exception e) {
			logger.info("client close: ".concat(socket.toString()).concat(",error:").concat(e.getMessage()==null?"":e.getMessage()));		
		}finally{
			synchronized (InvertBridgeManager.printBridgeThreads){
				PrintBridgeThread thread=InvertBridgeManager.printBridgeThreads.get(bridgeId);
				if(thread!=null){
					if(hashCode()==thread.hashCode()) InvertBridgeManager.printBridgeThreads.remove(bridgeId);
				}
			}					
			logger.info("client finished ".concat(socket.toString()));
			try{
				socket.close();										
			}catch(Exception e2){					
			}
			InvertBridgeManager.printBridgeThreadLocker.release();
		}
		bridgeState.setOnline(false);
		bridgeState.setDownTime(new Date());
	}
	public ByteBuffer printPdf(PrintParameters param) throws Exception {
		if(lock.tryLock(executeWaitTimeout,TimeUnit.MILLISECONDS)){
			try{
				return client.printPdf(param);
			}catch(PrintException e){
				throw new Exception(e.why);
			}finally{
				lock.unlock();
			}			
		}else throw new Exception("设备忙");
	}
	public String printPrinter(PrintParameters param) throws Exception {
		if(lock.tryLock(executeWaitTimeout,TimeUnit.MILLISECONDS)){
			try{
				return client.printPrinter(param);
			}catch(PrintException e){
				throw new Exception(e.why);
			}finally{
				lock.unlock();
			}			
		}else throw new Exception("设备忙");
	}

	public String printCommand(CommandPrintParameters param) throws Exception {
		if(lock.tryLock(executeWaitTimeout,TimeUnit.MILLISECONDS)){
			try{
				return client.printCommand(param);
			}catch(PrintException e){
				throw new Exception(e.why);
			}finally{
				lock.unlock();
			}
		}else throw new Exception("设备忙");
	}

	public List<RemotePrinter> listPrinters() throws Exception {
		if(lock.tryLock(executeWaitTimeout,TimeUnit.MILLISECONDS)){
			try{
				return client.listPrinters();
			}finally{
				lock.unlock();
			}			
		}else throw new Exception("设备忙");
	}
	public boolean pingPrinter(String printerName){
		try{
			if(lock.tryLock(executeWaitTimeout,TimeUnit.MILLISECONDS)){
				try{
					client.ping();
					return true;
				}finally{
					lock.unlock();
				}			
			}else throw new Exception("设备忙");
		}catch(Exception e){
			return false;
		}
	}	
}

